package com.lee.forum.services.impl;

import com.lee.forum.config.AppConfig;
import com.lee.forum.dao.ArticleMapper;
import com.lee.forum.dao.ArticleReplyMapper;
import com.lee.forum.model.Article;
import com.lee.forum.model.ArticleReply;
import com.lee.forum.model.Board;
import com.lee.forum.model.User;
import com.lee.forum.services.IArticleTopCacheService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
@Slf4j
@Service
public class ArticleTopCacheServiceImpl implements IArticleTopCacheService {
    // 定义一个Map集合用来存放一段时间内（10分钟、一小时）的修改（点赞/评论/浏览/删除）
    // 这里的map只是临时的，后期可以改造为消息队列，将修改信息存到消息队列中，这样即使服务器掉电，数据也不会丢失
    public static Map<String, Article> modifyMap = new ConcurrentHashMap<>();

    // 这里注意一个点：Resource根据名称注入（默认），找不到才按照类型注入。
    // @Autowrite默认根据类型注入(Spring中只有RedisTemplate和StringRedisTemplate两种)
    @Resource
    private RedisTemplate<String,Object> redisTemplate;

    @Autowired
    private ArticleMapper articleMapper;

    @Autowired
    private ArticleReplyMapper replyMapper;

    public void preloadCache() {
        // 日志计数器
        int count = 0;

        // 获取当前系统时间
        long hourKey = System.currentTimeMillis()/(1000*60*60);

        // 从redis查一下是否有数据，如果有直接返回，就不进行下面的初始化操作了
        Long size = redisTemplate.opsForZSet().size(AppConfig.HOT_ARTICLES_KEY+hourKey);
        if (size != 0) {
            return;
        }

        // 从数据库根据热度（评论+浏览量+点赞）查询前top10的文章信息
        List<Article> articles = articleMapper.selectTopnArticles(10);
        // 判断数据库是否有数据
        if (articles.size() == 0) {
            // 数据库也没数据，MyBatis返回集合长度为0，将redis中热榜置为null，便于后续判断(zset元素个数为一，并且标志位为emptyZset=0)
            redisTemplate.opsForZSet().add(AppConfig.HOT_ARTICLES_KEY+hourKey,"emptyZset",0.0);
        } else {
            // 数据库有数据
            for (Article article:articles) {
                count += addArticleToRedis(article);
            }
        }
        // 打印日志
        log.info("缓存预热完成，共写入redis "+ count + "条数据.");
    }

    /**
     * 添加文章以及文章附属的评论到redis中
     * @param article
     */
    private int addArticleToRedis(Article article) {
        int count = 0;

        // 获取当前系统时间
        long hourKey = System.currentTimeMillis()/(1000*60*60);

        // 1.先写入redis热榜缓存(这里还解决了一个bug，就是score，下面代码自己看)
//        double hotValue = getHotValue(article.getVisitCount(),article.getLikeCount(),article.getReplyCount());
        double hotValue = article.getScore();


        redisTemplate.opsForZSet().add(AppConfig.HOT_ARTICLES_KEY+hourKey,AppConfig.ARTICLES_KEY+article.getId(),hotValue);
        count ++;

        // 2.将文章详情写入redis
        // redis3写法
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_id",article.getUser().getId());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_avatarUrl",article.getUser().getAvatarUrl());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_nickname",article.getUser().getNickname());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_gender",article.getUser().getGender());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_isAdmin",article.getUser().getIsAdmin());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_state",article.getUser().getState());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"u_deleteState",article.getUser().getDeleteState());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"b_id",article.getBoard().getId());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"b_name",article.getBoard().getName());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"b_state",article.getBoard().getState());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"b_deleteState",article.getBoard().getDeleteState());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_id",article.getId());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_boardId",article.getBoarId());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_userId",article.getUserId());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_title",article.getTitle());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_content",article.getContent());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_visitCount",article.getVisitCount());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_replyCount",article.getReplyCount());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_likeCount",article.getLikeCount());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_state",article.getState());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_createTime",article.getCreateTime());
        redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+article.getId(),"a_updateTime",article.getUpdateTime());

        // 过期时间60分钟
        redisTemplate.expire(AppConfig.ARTICLES_KEY+article.getId(),60, TimeUnit.MINUTES);

        count ++;
        // redis5写法
        // ...
        // 3.将文章关联评论写入redis
        List<ArticleReply> articleReplies = replyMapper.selectByArticleId(article.getId());

        for (ArticleReply articleReply : articleReplies) {
            // 3.1因为有顺序，所以先写入list，保存顺序replayID
            redisTemplate.opsForList().rightPush(AppConfig.REPLAY_ARTICLE_KEY+article.getId(), String.valueOf(articleReply.getId()));

            // 过期时间60分钟
            redisTemplate.expire(AppConfig.REPLAY_ARTICLE_KEY+article.getId(),60, TimeUnit.MINUTES);

            // 3.2然后写入评论
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"u_id",articleReply.getUser().getId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"u_avatarUrl",articleReply.getUser().getAvatarUrl());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"u_nickname",articleReply.getUser().getNickname());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_id",articleReply.getId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_articleId",articleReply.getArticleId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_postUserId",articleReply.getPostUserId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_replyId",articleReply.getReplyId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_replyUserId",articleReply.getReplyUserId());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_content",articleReply.getContent());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_lickCount",articleReply.getLikeCount());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_state",articleReply.getState());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_createTime",articleReply.getCreateTime());
            redisTemplate.opsForHash().put(AppConfig.REPLAY_KEY+articleReply.getId(),"ar_updateTime",articleReply.getUpdateTime());

            // 过期时间60分钟
            redisTemplate.expire(AppConfig.REPLAY_KEY+articleReply.getId(),60, TimeUnit.MINUTES);
            count ++;
        }
        return count;
    }

    @Override
    public List<Article> getHotArticleLists(int n) {
        // 创建结果集
        List<Article> articles = new ArrayList<>();
        long hourKey = System.currentTimeMillis() / (1000 * 60 * 60);
        System.out.println("当前系统时间："+hourKey);
        Set<Object> range = redisTemplate.opsForZSet().reverseRange(AppConfig.HOT_ARTICLES_KEY+hourKey, 0, n-1); // 存在小bug，这里应该是n-1
        // 如果为空
        if (range == null || range.size() == 0) {
            return articles;
        }
        for (Object articleId : range) {
            Article article = new Article();
            article.setUser(new User());
            article.setBoard(new Board());
            // 1.数据整合

            Object uId = redisTemplate.opsForHash().get((String) articleId, "u_id");
            Object uAvatarUrl = redisTemplate.opsForHash().get((String) articleId, "u_avatarUrl");
            Object uNickname = redisTemplate.opsForHash().get((String) articleId, "u_nickname");
            Object aId =  redisTemplate.opsForHash().get((String) articleId, "a_id");
            Object aBoardId = redisTemplate.opsForHash().get((String) articleId, "a_boardId");
            Object aUserId = redisTemplate.opsForHash().get((String) articleId, "a_userId");
            Object aTitle = redisTemplate.opsForHash().get((String) articleId, "a_title");
            Object aVisitCount =  redisTemplate.opsForHash().get((String) articleId, "a_visitCount");
            Object aReplyCount = redisTemplate.opsForHash().get((String) articleId, "a_replyCount");
            Object aLikeCount = redisTemplate.opsForHash().get((String) articleId, "a_likeCount");
            Object aState = redisTemplate.opsForHash().get((String) articleId, "a_state");
            Object aCreateTime = redisTemplate.opsForHash().get((String) articleId, "a_createTime");
            Object aUpdateTime = redisTemplate.opsForHash().get((String) articleId, "a_updateTime");
            // 2."对象装配"
            article.getUser().setId(((Integer)uId).longValue());
            article.getUser().setAvatarUrl((String) uAvatarUrl);
            article.getUser().setNickname((String) uNickname);
            article.setId(((Integer) aId).longValue());
            article.setBoarId(((Integer) aBoardId).longValue());
            article.setUserId(((Integer) aUserId).longValue());
            article.setTitle((String) aTitle);
            article.setVisitCount((Integer) aVisitCount);
            article.setReplyCount((Integer) aReplyCount);
            article.setLikeCount((Integer) aLikeCount);
            article.setState(((Integer) aState).byteValue());
            article.setCreateTime((Date) aCreateTime);
            article.setUpdateTime((Date) aUpdateTime);

            articles.add(article);
        }
        return articles;
    }

    @Override
    public Article getHotArticleDetail(Long id) {

        // 1.查redis
        Object uId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_id");
        if (uId == null) {
            return null;
        }

        Object uAvatarUrl = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_avatarUrl");
        Object uNickname = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_nickname");
        Object uGender = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_gender");
        Object uIsAdmin = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_isAdmin");
        Object uState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_state");
        Object uDeleteState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_deleteState");
        Object bId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_id");
        Object bName = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_name");
        Object bState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_state");
        Object bDeleteState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_deleteState");
        Object aId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_id");
        Object aBoardId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_boardId");
        Object aUserId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_userId");
        Object aTitle = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_title");
        Object aContent = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_content");
        Object aVisitCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_visitCount");
        Object aReplyCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_replyCount");
        Object aLikeCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_likeCount");
        Object aState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_state");
        Object aCreateTime = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_createTime");
        Object aUpdateTime = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_updateTime");

        // 对象装配
        Article article = new Article();
        article.setUser(new User());
        article.setBoard(new Board());

        article.getUser().setId(((Integer)uId).longValue());
        article.getUser().setAvatarUrl((String) uAvatarUrl);
        article.getUser().setNickname((String) uNickname);
        article.getUser().setGender(((Integer) uGender).byteValue());
        article.getUser().setIsAdmin(((Integer) uIsAdmin).byteValue());
        article.getUser().setState(((Integer) uState).byteValue());
        article.getUser().setDeleteState(((Integer) uDeleteState).byteValue());

        article.getBoard().setId(((Integer) bId).longValue());
        article.getBoard().setName((String) bName);
        article.getBoard().setState(((Integer) bState).byteValue());
        article.getBoard().setDeleteState(((Integer) bDeleteState).byteValue());

        article.setId(((Integer) aId).longValue());
        article.setBoarId(((Integer) aBoardId).longValue());
        article.setUserId(((Integer) aUserId).longValue());
        article.setTitle((String) aTitle);
        article.setContent((String) aContent);
        article.setVisitCount((Integer) aVisitCount);
        article.setReplyCount((Integer) aReplyCount);
        article.setLikeCount((Integer) aLikeCount);
        article.setState(((Integer) aState).byteValue());
        article.setCreateTime((Date) aCreateTime);
        article.setUpdateTime((Date) aUpdateTime);

        return article;
    }

    public List<ArticleReply> getHotArticleReply(Long id) {
        String replylistKey = AppConfig.REPLAY_ARTICLE_KEY+id;

        // 1.查询redis
        List<Object> range = redisTemplate.opsForList().range(replylistKey, 0, -1);


        // 结果集
        List<ArticleReply> articleReplies = new ArrayList<>();

        // 2.判空
        if (range == null) {
            return articleReplies;
        }

        // 3.查询评论详情
        for (Object articleKey : range) {
            // 查询redis评论详情
            Object uId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY + (String) articleKey, "u_id");
            Object uAvatarUrl = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "u_avatarUrl");
            Object uNickname = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "u_nickname");
            Object arId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_id");
            Object arArticleId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_articleId");
            Object arPostUserId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_postUserId");
            Object arReplyId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_replyId");
            Object arReplyUserId = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_replyUserId");
            Object arContent = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_content");
            Object arLickCount = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_lickCount");
            Object arState = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_state");
            Object arCreateTime = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_createTime");
            Object arUpdateTime = redisTemplate.opsForHash().get(AppConfig.REPLAY_KEY +(String) articleKey, "ar_updateTime");

            // 对象装配
            ArticleReply articleReply = new ArticleReply();
            articleReply.setUser(new User());

            articleReply.getUser().setId(((Integer)uId).longValue());
            articleReply.getUser().setAvatarUrl((String) uAvatarUrl);
            articleReply.getUser().setNickname((String) uNickname);

            articleReply.setId(((Integer)arId).longValue());
            articleReply.setArticleId(((Integer)arArticleId).longValue());
            articleReply.setPostUserId(((Integer)arPostUserId).longValue());
            // 楼中楼功能字段，还没实现，暂时置空
            articleReply.setReplyId(null);
            articleReply.setReplyUserId(null);
            articleReply.setContent((String) arContent);
            articleReply.setLikeCount(((Integer)arLickCount));
            articleReply.setState(((Integer)arState).byteValue());
            articleReply.setCreateTime((Date) arCreateTime);
            articleReply.setUpdateTime((Date) arUpdateTime);

            articleReplies.add(articleReply);
        }

        return articleReplies;
    }

    public boolean updateHotArticleDetail(Article article) {
        Long id = article.getId();

        // 1.查redis
        Object uId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_id");
        if (uId == null) {
            return true;
        }
//        Object uAvatarUrl = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_avatarUrl");
//        Object uNickname = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_nickname");
//        Object uGender = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_gender");
//        Object uIsAdmin = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_isAdmin");
//        Object uState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_state");
//        Object uDeleteState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "u_deleteState");
//        Object bId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_id");
//        Object bName = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_name");
//        Object bState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_state");
//        Object bDeleteState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "b_deleteState");
//        Object aId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_id");
//        Object aBoardId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_boardId");
//        Object aUserId = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_userId");
        try {
            redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY + id, "a_title", article.getTitle());
            redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY + id, "a_content", article.getContent());
        } catch (Exception e) {
            log.error("redis更新Article异常");
            return false;
        }

//        Object aVisitCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_visitCount");
//        Object aReplyCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_replyCount");
//        Object aLikeCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_likeCount");
//        Object aState = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_state");
//        Object aCreateTime = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_createTime");
//        Object aUpdateTime = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + id, "a_updateTime");

        return true;
    }


    // 锁对象
    private static Object locker1 = new Object();

    @Override
    public Boolean articleAddLikeCount(Article article) {
        // 1.先查询map中是否存在文章信息
        Article articleVO = modifyMap.get("addL:"+getMapKey(article.getId()));
        // 加锁防止并发问题
        synchronized (locker1) {
            if (articleVO == null) {
                // 1.1如果不存在文章
                // 创建对象
                articleVO = article;
                // 初始化score
                articleVO.setScore(0.0);
            }
            // 计算 score
            double oldScore = articleVO.getScore();
            articleVO.setScore(getHotValue(0,1,0) + oldScore);
            // 计算点赞数
            articleVO.setLikeCount(article.getLikeCount() + 1);
        }

        // 插入map
        modifyMap.put("addL"+getMapKey(article.getId()),articleVO);

        return true;
    }

    // 锁对象
    private static Object locker2 = new Object();

    @Override
    public Boolean articleAddVisitCount(Article article) {
        // 1.先查询map中是否存在文章信息
        Article articleVO = modifyMap.get("addV"+getMapKey(article.getId()));
        // 加锁防止并发问题
        synchronized (locker2) {
            if (articleVO == null) {
                // 1.1如果不存在文章
                // 创建对象
                articleVO = article;
                // 初始化score
                articleVO.setScore(0.0);
                System.out.println("score1:"+articleVO.getScore());
            }
            // 计算 score
            double oldScore = articleVO.getScore();
            articleVO.setScore(getHotValue(1,0,0) + oldScore);
            // 计算浏览量
            articleVO.setVisitCount(article.getVisitCount() + 1);
        }

        // 插入map
        modifyMap.put("addV"+getMapKey(article.getId()),articleVO);
        return null;
    }

    // 锁对象
    private static Object locker3 = new Object();

    @Override
    public Boolean articleAddReplyCount(Article article) {
        // 1.先查询map中是否存在文章信息
        Article articleVO = modifyMap.get("addR"+getMapKey(article.getId()));
        // 加锁防止并发问题
        synchronized (locker3) {
            if (articleVO == null) {
                // 1.1如果不存在文章
                // 创建对象
                articleVO = article;
                // 初始化score
                articleVO.setScore(0.0);
            }
            // 计算 score
            double oldScore = articleVO.getScore();
            articleVO.setScore(getHotValue(0,0,1) + oldScore);
            // 计算回复数
            articleVO.setReplyCount(article.getReplyCount() + 1);
        }

        // 插入map
        modifyMap.put("addR"+getMapKey(article.getId()),articleVO);
        return null;
    }

    @Override
    public Boolean articleDelete(Article article) {
        // 直接remove即可
        modifyMap.put("dele"+getMapKey(article.getId()), article);
        return true;
    }

    /**
     * 热度计算规则
     * @return
     */
    private double getHotValue(Integer visitCount,Integer likeCount,Integer replyCount) {
        return (visitCount + likeCount + replyCount) * 100.0;
    }

    /**
     * 获取 mapKey
     * @param id
     * @return
     */
    private String getMapKey(Long id) {
        long hourKey = System.currentTimeMillis()/(1000*60*60);
        // 1.先查询map中是否存在文章信息
       return hourKey+":"+AppConfig.ARTICLES_KEY+id;
    }

    @Override
    public void updateTopRedis() {
        // 使用时间戳时更，这里就不实现了
    }

    @Override
    public void refreshTopRedis() {
        log.info("刷新前Map集合中元素个数为："+modifyMap.size());
        log.info("刷新前Map集合中遗留元素为："+modifyMap);
        int count = 0;
        // 数据暂定为每分钟刷新，保证高可用的同时需要牺牲一定的一致性（可以优化，使用消息队列，一致性也不能保证）
        for (Map.Entry<String,Article> entry : modifyMap.entrySet()) {
            // 1.获取到key和value
            Article articleVO = entry.getValue();
            // 2.key的格式为：____hourkey:AppConfig.ARTICLES_KEY:id，所以这里获取时间搓
            String operateKey = entry.getKey().substring(0,4);
            String hourKey = entry.getKey().substring(4, 10);
            // 3.遍历map集合，写入redis（zincrby命令如果存在则加分，不存在则创建）
            Double aDouble = redisTemplate.opsForZSet().incrementScore(AppConfig.HOT_ARTICLES_KEY + hourKey,
                    AppConfig.ARTICLES_KEY + articleVO.getId(),
                    articleVO.getScore());

            count ++;
            // 4.假设redis中不存在score为0的热帖，开始插入操作
            if (aDouble == articleVO.getScore() && !operateKey.equals("dele")) {
                // 说明之前没有，是创建插入数据
                count += addArticleToRedis(articleVO);
            } else {
                // 说明是更新数据
                if (operateKey.equals("addL")) {
                    // 点赞操作
                    // 根据文章Id找到文章详情，并更新点赞
                    // Object aLikeCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + articleVO.getId(), "a_likeCount");
                    // redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+articleVO.getId(),"a_likeCount",(Integer)aLikeCount + 1);
                    redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+articleVO.getId(),"a_likeCount",articleVO.getLikeCount());
                    count ++;
                } else if (operateKey.equals("addV")) {
                    // 浏览操作
                    // 根据文章Id找到文章详情，并更新浏览
                    // Object aVisitCount = redisTemplate.opsForHash().get(AppConfig.ARTICLES_KEY + articleVO.getId(), "a_visitCount");
                    // redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+articleVO.getId(),"a_visitCount",(Integer)aVisitCount + 1);
                    redisTemplate.opsForHash().put(AppConfig.ARTICLES_KEY+articleVO.getId(),"a_visitCount",articleVO.getVisitCount());
                    count ++;
                } else if (operateKey.equals("addR")) {
                    // 评论操作
                    // 根据文章Id找到文章评论列表，并增加评论
                    // 先删除list缓存
                    // (其实考虑到评论的实时性，这里应该删除了，但是考虑到起初的设计，还是先存着吧)
                    redisTemplate.delete(AppConfig.REPLAY_ARTICLE_KEY+articleVO.getId());
                    count += addArticleToRedis(articleVO);

                } else if (operateKey.equals("dele")) {
                    // 删除操作
                    // 根据文章Id查询热榜中有无文章
                    Long rank = redisTemplate.opsForZSet().rank(AppConfig.HOT_ARTICLES_KEY + hourKey, AppConfig.ARTICLES_KEY + articleVO.getId());
                    if (rank == null) {
                        return;
                    } else {
                        // 删除hot
                        redisTemplate.opsForZSet().remove(AppConfig.HOT_ARTICLES_KEY + hourKey,AppConfig.ARTICLES_KEY+articleVO.getId());
                        count ++;
                        // 删除detail
                        redisTemplate.delete(AppConfig.ARTICLES_KEY+articleVO.getId());
                        count ++;
                        // 删除reply,删除list
                        List<Object> range = redisTemplate.opsForList().range(AppConfig.REPLAY_ARTICLE_KEY + articleVO.getId(), 0, -1);
                        for (Object key : range) {
                            String replyKey = AppConfig.REPLAY_KEY+(String) key;
                            redisTemplate.delete(replyKey+articleVO.getId());
                            count ++;
                        }
                        redisTemplate.delete(AppConfig.REPLAY_ARTICLE_KEY+articleVO.getId());
                        count ++;
                    }
                }
            }
            // 防止内存泄漏导致最终内存溢出问题，这里需要使用完后移除元素
            modifyMap.remove(entry.getKey());

        }
        log.info("刷新redis数据："+count);
        log.info("刷新后Map集合中元素个数为："+modifyMap.size());
        log.info("刷新后Map集合中遗留元素为："+modifyMap);
    }
}
